package com.alex.statistics.method.spatialAnalysis;

import com.alex.statistics.pojo.request.spatialAnalysis.LocalLisaRequestBack;
import com.alex.statistics.pojo.result.spatialAnalysis.LocalLisaResultBack;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.stream.IntStream;

@Service
public class LocalLisaServiceBack {

    private static final String HH = "HH"; // 高-高聚类
    private static final String HL = "HL"; // 高-低异常
    private static final String LH = "LH"; // 低-高异常
    private static final String LL = "LL"; // 低-低聚类
    private static final String NS = "NS"; // 不显著

    public LocalLisaResultBack calculate(LocalLisaRequestBack request) {
        long startTime = System.currentTimeMillis();
        validateInput(request);

        List<Double> values = request.getValues();
        double[][] weights = request.getSpatialWeights();
        double alpha = request.getSignificanceLevel();
        int permutations = request.getPermutations();

        // 1. 计算均值和标准差
        double mean = values.stream().mapToDouble(Double::doubleValue).average().orElse(0);
        double stdDev = Math.sqrt(values.stream()
                .mapToDouble(v -> Math.pow(v - mean, 2))
                .average().orElse(0));

        // 2. 并行计算每个位置的指标
        List<LocalLisaResultBack> results = IntStream.range(0, values.size()).parallel()
                .mapToObj(index -> {  // 这里使用index作为参数名
                    double localI = computeLocalMoranI(index, values, weights, mean);
                    double pValue = computePValue(index, values, weights, localI, permutations);
                    String type = determineClusterType(index, values, weights, localI, alpha, mean);
                    return new LocalLisaResultBack(
                            Collections.singletonList(type),
                            Collections.singletonList(localI),
                            Collections.singletonList(pValue),
                            alpha,
                            0
                    );
                }).toList();

        // 3. 合并结果
        LocalLisaResultBack finalResult = new LocalLisaResultBack();
        finalResult.setSignificanceLevel(alpha);
        finalResult.setComputeTime(System.currentTimeMillis() - startTime);

        finalResult.setClusterTypes(results.stream()
                .flatMap(r -> r.getClusterTypes().stream()).toList());
        finalResult.setLocalMoranValues(results.stream()
                .flatMap(r -> r.getLocalMoranValues().stream()).toList());
        finalResult.setPValues(results.stream()
                .flatMap(r -> r.getPValues().stream()).toList());

        return finalResult;
    }

    private double computeLocalMoranI(int index, List<Double> values, double[][] weights, double mean) {
        double zi = (values.get(index) - mean);
        double sum = 0;
        double weightSum = 0;

        for (int j = 0; j < values.size(); j++) {
            if (index != j) {  // 使用index而不是i
                double zj = (values.get(j) - mean);
                sum += weights[index][j] * zi * zj;
                weightSum += weights[index][j];
            }
        }

        return weightSum != 0 ? sum / weightSum : 0;
    }

    private double computePValue(int index, List<Double> values, double[][] weights,
                                 double observedI, int permutations) {
        if (permutations <= 0) return Double.NaN;

        int countExtreme = 0;
        Random random = new Random();

        for (int p = 0; p < permutations; p++) {
            // 随机置换邻居值
            List<Double> shuffledValues = new ArrayList<>(values);
            Collections.shuffle(shuffledValues, random);

            double randomI = computeLocalMoranI(index, shuffledValues, weights,
                    shuffledValues.stream().mapToDouble(Double::doubleValue).average().orElse(0));

            if (Math.abs(randomI) >= Math.abs(observedI)) {
                countExtreme++;
            }
        }

        return (countExtreme + 1.0) / (permutations + 1.0); // 避免p=0
    }

    private String determineClusterType(int index, List<Double> values,
                                        double[][] weights, double localI,
                                        double alpha, double mean) {
        if (Double.isNaN(localI))
            return NS;

        double zi = (values.get(index) - mean);
        double neighborSum = 0;
        double weightSum = 0;

        for (int j = 0; j < values.size(); j++) {
            if (index != j) {  // 使用index而不是i
                neighborSum += weights[index][j] * values.get(j);
                weightSum += weights[index][j];
            }
        }

        double meanNeighbor = weightSum != 0 ? neighborSum / weightSum : 0;
        double zNeighbor = meanNeighbor - mean;

        if (zi > 0 && zNeighbor > 0) return HH;
        if (zi > 0 && zNeighbor < 0) return HL;
        if (zi < 0 && zNeighbor > 0) return LH;
        if (zi < 0 && zNeighbor < 0) return LL;
        return NS;
    }

    private void validateInput(LocalLisaRequestBack request) {
        if (request.getValues() == null || request.getValues().isEmpty()) {
            throw new IllegalArgumentException("观测值列表不能为空");
        }
        if (request.getSpatialWeights() == null ||
                request.getSpatialWeights().length != request.getValues().size()) {
            throw new IllegalArgumentException("空间权重矩阵尺寸与观测值数量不匹配");
        }
        if (request.getSignificanceLevel() <= 0 || request.getSignificanceLevel() >= 1) {
            throw new IllegalArgumentException("显著性水平必须在(0,1)区间内");
        }
    }
}